﻿using GoPlay.Models;
using GoPlay.Controllers;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.SessionState;
//using GoPlay.Tests.Models;

namespace GoPlay.Tests.Controllers
{
    [TestClass]
    public class TeamsControllerTests
    {
        [TestMethod]
        public void Team_Index_GET_AsksForIndexView()
        {
            // Arrange
            var controller = GetTeamsController(new InMemoryRepository<Team>());

            // Act
            ViewResult result = controller.Index() as ViewResult;

            // Assert
            Assert.AreNotEqual(null, result);
        }

        [TestMethod]
        public void Team_Index_GET_RetrievesAllTeamsFromRepositoryForThisUser()
        {
            // Arrange
            Team t = new Team { Name = "test", UserId = 1 };
            InMemoryRepository<Team> repository = new InMemoryRepository<Team>();
            repository.Insert(t);
            t = new Team { Name = "test2", UserId = 2 };
            repository.Insert(t);
            var controller = GetTeamsController(repository);

            // Act
            ViewResult result = controller.Index() as ViewResult;

            // Assert
            var model = (IEnumerable<Team>)result.Model;
            //CollectionAssert.Contains(model.ToList(), t);
            Assert.AreEqual(model.Count(), 1);
            Assert.AreEqual("test", model.First().Name);
        }

        [TestMethod]
        public void Team_Create_POST_PutsValidTeamIntoRepository()
        {
            // Arrange
            InMemoryRepository<Team> repository = new InMemoryRepository<Team>();
            TeamsController controller = GetTeamsController(repository);
            Team Team = new Team { Name = "test", UserId = 1 };

            // Act
            controller.Create(Team);

            // Assert
            IEnumerable<Team> Teams = repository.GetAllByUserId(1);
            Assert.IsTrue(Teams.Contains(Team));
        }

        [TestMethod]
        public void Team_Create_POST_ReturnsRedirectOnSuccess()
        {
            // Arrange
            TeamsController controller = GetTeamsController(new InMemoryRepository<Team>());
            Team Team = new Team { Name = "test", UserId = 1 };

            // Act
            var result = (RedirectToRouteResult)controller.Create(Team);

            // Assert
            Assert.AreEqual("Index", result.RouteValues["action"]);
        }

        [TestMethod]
        public void Team_Create_POST_ReturnsViewIfModelStateIsNotValid()
        {
            // Arrange
            TeamsController controller = GetTeamsController(new InMemoryRepository<Team>());
            // Simply executing a method during a unit test does just that - executes a method, and no more. 
            // The MVC pipeline doesn't run, so binding and validation don't run.
            controller.ModelState.AddModelError("", "mock error message");
            Team Team = new Team { Name = "test", UserId = 1 };

            // Act
            var result = (ViewResult)controller.Create(Team);

            // Assert
            Assert.AreEqual("GoPlay.Models.Team", result.ViewData.Model.ToString());
        }

        [TestMethod]
        public void Team_Create_POST_ReturnsViewIfRepositoryThrowsException()
        {
            // Arrange
            InMemoryRepository<Team> repository = new InMemoryRepository<Team>();
            Exception exception = new Exception();
            repository.ExceptionToThrow = exception;
            TeamsController controller = GetTeamsController(repository);
            Team Team = new Team { Name = "test", UserId = 1 };

            // Act
            var result = controller.Create(Team) as ViewResult;

            // Assert
            ModelState modelState = result.ViewData.ModelState[""];
            Assert.IsNotNull(modelState);
            Assert.IsTrue(modelState.Errors.Any());
            Assert.AreEqual(exception, modelState.Errors[0].Exception);
        }

        [TestMethod]
        public void Team_Edit_POST_UpdatesTeamInRepository()
        {
            // Arrange
            InMemoryRepository<Team> repository = new InMemoryRepository<Team>();
            Team team = new Team { Id=1, Name = "test", UserId = 1 };
            repository.Insert(team);
            TeamsController controller = GetTeamsController(repository);
            team = repository.GetByID(1);
            team.Name += "-e";

            // Act
            controller.Edit(team);

            // Assert
            IEnumerable<Team> teams = repository.GetAllByUserId(1);
            Assert.AreEqual("test-e", teams.First().Name);
        }

        [TestMethod]
        public void Team_Edit_POST_ReturnsRedirectOnSuccess()
        {
            // Arrange
            InMemoryRepository<Team> repository = new InMemoryRepository<Team>();
            Team team = new Team { Id = 1, Name = "test", UserId = 1 };
            repository.Insert(team);
            TeamsController controller = GetTeamsController(repository);
            team = repository.GetByID(1);
            team.Name += "-e";

            // Act
            //controller.Edit(team);
            var result = (RedirectToRouteResult)controller.Edit(team);

            // Assert
            Assert.AreEqual("Index", result.RouteValues["action"]);
            //Assert.Inconclusive();
        }

        [TestMethod]
        public void Team_Edit_POST_ReturnsViewIfModelStateIsNotValid()
        {
            // Arrange
            InMemoryRepository<Team> repository = new InMemoryRepository<Team>();
            TeamsController controller = GetTeamsController(repository);
            // Simply executing a method during a unit test does just that - executes a method, and no more. 
            // The MVC pipeline doesn't run, so binding and validation don't run.
            Team team = new Team { Id = 1, Name = "test", UserId = 1 };
            repository.Insert(team);
            team = repository.GetByID(1);
            team.Name += "-e";
            controller.ModelState.AddModelError("", "mock error message");

            // Act
            var result = (ViewResult)controller.Edit(team);

            // Assert
            Assert.AreEqual("GoPlay.Models.Team", result.ViewData.Model.ToString());
            //Assert.Inconclusive();
        }

        [TestMethod]
        public void Team_Edit_POST_ReturnsViewIfRepositoryThrowsException()
        {
            // Arrange
            InMemoryRepository<Team> repository = new InMemoryRepository<Team>();
            TeamsController controller = GetTeamsController(repository);
            Team team = new Team { Id=1, Name = "test", UserId = 1 };
            repository.Insert(team);
            team = repository.GetByID(1);
            team.Name += "-e";
            Exception exception = new Exception();
            repository.ExceptionToThrow = exception;

            // Act
            var result = (ViewResult)controller.Edit(team);

            // Assert
            ModelState modelState = result.ViewData.ModelState[""];
            Assert.IsNotNull(modelState);
            Assert.IsTrue(modelState.Errors.Any());
            Assert.AreEqual(exception, modelState.Errors[0].Exception);
            //Assert.Inconclusive();
        }



        private static TeamsController GetTeamsController(IRepository<Team> repository)
        {
            TeamsController controller = new TeamsController(repository);

            var sessionItems = new SessionStateItemCollection();
            sessionItems["UserId"] = "1";
            sessionItems["UserName"] = "chipirek";
            controller.ControllerContext = new MvcFakes.FakeControllerContext(controller, sessionItems);
            return controller;
        }

    }
}
